package servlet;

import java.io.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.Date;
import java.util.Map;

import javax.mail.BodyPart;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.sun.mail.pop3.POP3Folder;

import util.*;
import bll.*;
import model.*;
import dal.*;

/**
 * Servlet implementation class CompanyServlet
 */
@WebServlet(urlPatterns = { "/Chat", "/chat", "/ChatServlet", "/chatServlet" }, initParams = { @WebInitParam(name = "action", value = "") })
public class ChatServlet extends HttpServlet {
	
	
	
	
	private static final long serialVersionUID = 1L;
	
	private static Map chattingMsg;//caching message
	private static Map contactList;//used for caching all contacts. It helps rapidly access a contact by its username
	private static List<Contact> curOnlineList;//used for caching online contacts
	

	/**
	 * Default constructor.
	 */
	public ChatServlet() {
		// TODO Auto-generated constructor stub
		if (chattingMsg == null)
		{
			chattingMsg = new Hashtable();
		}
		if (contactList == null)
		{
			contactList = new Hashtable();
			List<Contact> contacts = getContactList();
			for (Contact contact : contacts)
			{
				contactList.put(contact.getUserName(), contact);
			}
		}

		if (curOnlineList == null)
		{
			curOnlineList = new ArrayList<Contact>();
			//
		}
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");

		String strAction = request.getParameter("action");
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");

		String strAction = request.getParameter("action");

		if (strAction == null)
			return;
		
		
		response.setContentType("text/xml; charset=UTF-8");
		
		if (strAction.equalsIgnoreCase("retrieve"))
		{
			//Putting the following code here is for optimization purpose 
			String strReceiver = request.getParameter("receiver");
			refreshContactStatus(strReceiver);

			
			List<ChatModel> msgs = popMessages(strReceiver);
			String str = createXMLMessageString(msgs);
			msgs.clear();
			//System.out.println("Pop all messages of " + strReceiver);
			PrintWriter out = response.getWriter();
			out.println(str);
			return;
		}
		else if (strAction.equalsIgnoreCase("send"))
		{
			String strSender = request.getParameter("sender");
			String strReceiver = request.getParameter("receiver");
			String strMessage = request.getParameter("message");
			
			ChatModel obj = new ChatModel();
			obj.setSender(strSender);
			obj.setReceiver(strReceiver);
			Date date = new Date();
			obj.setSentDate(date);
			
			obj.setMessage(strMessage);
			
			GenericDal<ChatModel, ChatModelExt> dal = new GenericDal<ChatModel, ChatModelExt>(new ChatModel());
			
			int res = 0;
			try {
				res = dal.insert(obj);
				pushMessage(strSender, strReceiver, strMessage);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			if (res > 0) {
				// TODO : success
				//PrintWriter out = response.getWriter();
				//out.print("");
				
			} else {
				// TODO : processing error here
			}
		}
		else if (strAction.equalsIgnoreCase("getContactList"))
		{
			//Use session to get sender username
			HttpSession session = request.getSession(true);
			String userName = (String) session.getAttribute("username");

			
			//Temporary use the follow in function for testing only
			String strContactList = createXMLContactList(getOnlineContactList(), userName);
			PrintWriter out = response.getWriter();
			out.println(strContactList);
			return;
		}
		else if (strAction.equalsIgnoreCase("getOnlineList"))
		{
			//Use session to get sender username
			HttpSession session = request.getSession(true);
			String userName = (String) session.getAttribute("username");

			//String strSender = request.getParameter("sender");
			String strContactList = createXMLContactList(getOnlineContactList());
			PrintWriter out = response.getWriter();
			out.println(strContactList);
			//System.out.println(strContactList);
			return;
		}
	}
	
	private boolean refreshContactStatus(String userName)
	{
		//System.out.println("RefreshContactStatus: " + userName);
		if (userName == null || userName.trim() == "")
		{
			return false;
		}
		
		boolean isNewContact = true;
		List<Contact> list = getOnlineContactList();
		//refresh the curOnlineList
		Date now = new Date();
		List<Integer> toDeleteList = new ArrayList<Integer>();
		for (int i = 0; i < list.size(); i++)
		{
			Contact c = list.get(i);
			Date d = c.getLastTimeAccess();
			if (c.getUserName().equalsIgnoreCase(userName))
			{
				c.setLastTimeAccess(new Date());
				isNewContact = false;
			}
			else if (now.getTime() - d.getTime() > 60000)
			{
				//mark to remove from online list
				toDeleteList.add(i);
			}
		}
		boolean isExisted = contactList.containsKey(userName);
		//System.out.println("existed user: " + isExisted + ", isNewContact: " + isNewContact);
		if (isNewContact == true && contactList.containsKey(userName))
		{
			Contact contact = (Contact)contactList.get(userName);
			contact.setLastTimeAccess(new Date());
			addToOnlineList(contact);
		}
		
		//Delete marked contact
		for (int idx : toDeleteList)
		{
			curOnlineList.remove(idx);
		}
		
		return isNewContact;
	}
	
	private void addToOnlineList(Contact c)
	{
		curOnlineList.add(c);
	}
	
	private boolean pushMessage(String strSender, String strReceiver, String strMessage)
	{
		//if (chattingMsg.get("")
		boolean isNewReceiver = false;
		MessageList list;
		if (chattingMsg.containsKey(strReceiver))
		{
			list = (MessageList)chattingMsg.get(strReceiver);
		}
		else
		{
			list = new MessageList();
			chattingMsg.put(strReceiver, list);
			isNewReceiver = true;
		}
		list.push(strSender, strReceiver, strMessage);
		return isNewReceiver;
	}
	
	private List<ChatModel> popMessages(String receiver)
	{
		Object obj = chattingMsg.get(receiver);
		List<ChatModel> list = new ArrayList<ChatModel>();
		if (obj != null)
		{
			MessageList msgs = (MessageList) obj;
			list = msgs.getAll();
		}
		return list;
	}
	
	private String createXMLMessageString(List<ChatModel> msgs) 
	{
		try
		{
			DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
			Document doc = docBuilder.newDocument();
			Element root = doc.createElement("MessageList");
			doc.appendChild(root);
			
			for (int i = 0; i < msgs.size(); i++) 
			{
				ChatModel msg = msgs.get(i);
				
				Element messageItem = doc.createElement("MessageItem");// EmailItem
				root.appendChild(messageItem);
				
				Element sender = doc.createElement("Sender");// Subject
				sender.setTextContent(msg.getSender());
				messageItem.appendChild(sender);
	
				Element sentDate = doc.createElement("SentDate");
				sentDate.setTextContent(util.DateTimeUtil.getDateTimeString(msg.getSentDate()));
				messageItem.appendChild(sentDate);
	
				Element content = doc.createElement("Message");
				content.setTextContent(msg.getMessage());
				messageItem.appendChild(content);
			}
	
			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();
			trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");
	
			// create string from xml tree
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			DOMSource source = new DOMSource(doc);
			trans.transform(source, result);
			
			String xmlString = sw.toString();
			return xmlString;
		}
		catch (Exception ex)
		{
			return "<MessageList value=\"error\"/>";
		}
	}
	
	private String createXMLContactList(List<Contact> contacts) 
	{
		return createXMLContactList(contacts, "");
	}
	
	private String createXMLContactList(List<Contact> contacts, String exceptedUser) 
	{
		try
		{
			DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
			Document doc = docBuilder.newDocument();
			Element root = doc.createElement("ContactList");
			doc.appendChild(root);
			
			for (int i = 0; i < contacts.size(); i++) 
			{
				Contact contact = contacts.get(i);
				//System.out.println(i + ": " + contact.getUserName() + " " + exceptedUser);
				if (contact.getUserName().equalsIgnoreCase(exceptedUser))
				{
					//System.out.println(contact.getUserName());
					continue;
				}
				Element contactItem = doc.createElement("ContactItem");
				root.appendChild(contactItem);
				
				Element displayName = doc.createElement("DisplayName");
				displayName.setTextContent(contact.getDisplayName());
				contactItem.appendChild(displayName);
				
				Element userName = doc.createElement("UserName");
				userName.setTextContent(contact.getUserName());
				contactItem.appendChild(userName);
				
				Element status = doc.createElement("Status");
				status.setTextContent(contact.getStatus());
				contactItem.appendChild(status);
			}
	
			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();
			trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");
	
			// create string from xml tree
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			DOMSource source = new DOMSource(doc);
			trans.transform(source, result);
			
			String xmlString = sw.toString();
			return xmlString;
		}
		catch (Exception ex)
		{
			return "<ContactList value=\"error\"/>";
		}
	}
	
	public List<Contact> getOnlineContactList()
	{
		//System.out.print("Online contacts: ");
//		for (Contact c : curOnlineList)
//		{
//			System.out.print(c.getUserName() + ", ");
//		}
		//System.out.println("");		
		return curOnlineList;
	}
	
	public List<Contact> getContactList()
	{
		List<Contact> list = new ArrayList<Contact>();
		Contact c = new Contact();
		c.setDisplayName("User");
		c.setUserName("user");
		c.setStatus("Miss s.o");
		
		
		Contact c2 = new Contact();
		c2.setDisplayName("Admin");
		c2.setUserName("admin");
		c2.setStatus("Cai gi the heo?");
		
		list.add(c);
		list.add(c2);
		
		return list;
	}
	
	public class MessageList
	{
		
		List<ChatModel> messages;

		public MessageList()
		{
			messages = new ArrayList<ChatModel>();
		}
		
		public void clearAll()
		{
			messages.clear();
		}
		
		public void push(ChatModel msg)
		{
			messages.add(msg);
		}
		
		public void push(String strSender, String strReceiver, String strMsg)
		{
			ChatModel msg =  new ChatModel();
			msg.setSender(strSender);
			msg.setReceiver(strReceiver);
			msg.setMessage(strMsg);
			msg.setSentDate(new Date());
			messages.add(msg);
		}
		
		public List<ChatModel> getAll()
		{
			return messages;
		}
		
		public String getXMLString()
		{
			return "";
		}
		
		public boolean saveToFile()
		{
			return false;
		}
	}
	
	class Contact
	{
		private String strUserName;
		private String strDisplayName;
		private String strStatus;
		private Date lastTimeAccess;
		
		public Contact()
		{
			strUserName = "";
			strDisplayName = "";
			strStatus = "";
			Date lastTimeAccess = new Date();

			//Cheat code
			lastTimeAccess.setYear(lastTimeAccess.getYear() - 1);
			//System.out.println(lastTimeAccess.getYear() + 1900);
		}
		
		
		public String getUserName()
		{
			return strUserName;
		}

		public boolean setUserName(String value)
		{
			if (value == null || value.trim() == "")
			{
				return false;
			}
			strUserName = value.trim();
			return true;
		}
		
		public String getDisplayName()
		{
			return strDisplayName;
		}

		public boolean setDisplayName(String value)
		{
			if (value == null || value.trim() == "")
			{
				return false;
			}
			strDisplayName = value.trim();
			return true;
		}
		
		public String getStatus()
		{
			return strStatus;
		}

		public boolean setStatus(String value)
		{
			if (value == null || value.trim() == "")
			{
				return false;
			}
			strStatus = value.trim();
			return true;
		}
		
		public Date getLastTimeAccess()
		{
			return lastTimeAccess;
		}

		public boolean setLastTimeAccess(Date value)
		{
			if (value == null)
			{
				return false;
			}
			lastTimeAccess = value;
			return true;
		}

	}
	
	
}

